#include "gwbase/base/buffer.h"

#include <sys/uio.h>
#include "gwbase/base/logging.h"

namespace dicom {

Buffer::~Buffer() {
}

void Buffer::MakeRoom(size_t len) {
  if (appendable_bytes() >= len)
    return;

  if (prependable_bytes() + appendable_bytes() >= len + kPrependSpace) {
    assert (prependable_bytes() > kPrependSpace);
    size_t avail_data = size();
    std::copy(begin(), end(), head()+kPrependSpace);
    read_index_ = kPrependSpace;
    write_index_ = read_index_ + avail_data ;
  } else {
    size_t next_size = std::max(buf_.size()*2, write_index_ + len);
    buf_.resize(next_size);
  }
}

ssize_t Buffer::ReadFd(int fd) {
  const ssize_t kExtraBuf = 64 * 1024;
  char extra[kExtraBuf];
  ssize_t writable = appendable_bytes();
  const int iovcnt = (writable > kExtraBuf) ? 1 : 2; // most 128KB per read

  struct iovec iov[2];
  iov[0].iov_base = end();
  iov[0].iov_len = writable;
  iov[1].iov_base = extra;
  iov[1].iov_len = kExtraBuf;

  do { 
    ssize_t n = ::readv(fd, iov, iovcnt);
    if (n < 0) {
      if (errno == EINTR)
        continue;
      LOG_FATAL << "readv() " << PosixErrorMsg(errno);
      return n;
    } else if (n <= writable) {
      write_index_ += static_cast<uint32_t>(n);
      return n;
    } else {
      LOG_TRACE << "extra buffer read: " << n-writable;
      write_index_ = static_cast<uint32_t>(buf_.size());
      Append(extra, n - writable);
      return n;
    }
  } while (true);
}

}
